package grex;

import grex.fitnessfunctions.IFitnessFunction;
import grex.fitnessfunctions.ErrorFunctions.ErrorCalculator;
import grex.fitnessfunctions.ErrorFunctions.IErrorFunction;
import grex.fitnessfunctions.FitnessFunction;

import grex.genes.Gene;
import grex.genes.GeneException;
import grex.genes.ITerminal;
import grex.genes.If;
import grex.genes.ProbTarget;
import grex.genes.TargetPred;

import java.io.Serializable;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

public class GP
        implements Cloneable,Serializable, IPredictiveModel {
    
    Gene head;
    double length;
    private int depth;
    private double fitness, testFitness = 0;
    public double TP=0,TN=0,FP=0,FN=0;
    
    private boolean F1=false;
    
    public ArrayList trainOutput = new ArrayList();
    private double trainAcc=-1,testAcc=-1,valAcc=-1,trainAUC=-1,valAUC=-1,testAUC=-1,trainBRIER=-1,valBRIER=-1,testBRIER=-1;
    private Options ops;
   // FitnessFunctionFactory fitnessFactory;
    FitnessFunction fitnessFunction;
    Environment environment;
    PredictionContainer pcTrain,pcVal,pcTest;
    private double[][] orgTrain,orgVal,orgTest;
    private String fold;
    private double likness=-1;
    private ErrorCalculator errorCalculator;

    public double[] calcSensitivity() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private class AdjustedFitnessTask implements Runnable,Serializable {

        public AdjustedFitnessTask() {

        }


        public void run() {
            try {
                calcAdjustedFitness();
            } catch (GeneException ex) {
                ErrorManager.getInstance().reportError(ex);
            } catch (BNFException ex) {
                ErrorManager.getInstance().reportError(ex);
            }
            
        }
    }
    private AdjustedFitnessTask adjustedFitnessTask = new AdjustedFitnessTask();


    //used by clone gp
    private GP(double length, int depth, double fitness, double testFitness,
            PredictionContainer pcTrain, PredictionContainer pcVal, PredictionContainer pcTest,Environment environment,String fold){
        this.environment=environment;
        this.length = length;
        this.depth = depth;
        this.fitness = fitness;
        this.testFitness = testFitness;
        this.pcTest=pcTest;
        this.pcTrain=pcTrain;
        this.pcVal=pcVal;
        trainAcc=testAcc=valAcc=-1;
        this.fold=""+fold;
        errorCalculator = environment.getErrorCalculator();
        initFitnessFunction(environment.getOptions());
    }
       
    public GP(Environment environment, int creationType, int maxDepth) throws
            BNFException, GeneException {
        this.environment = environment;
        environment.getBNF().createGP(this, creationType, maxDepth);
        initPredictionContainers(environment.getTrainData(),environment.getValData(), environment.getTestData());
        length = calcNrOfNodes(head);
        trainAcc=testAcc=valAcc=-1;
        fold=""+ops.getTestFOLD();  
        errorCalculator = environment.getErrorCalculator();
        initFitnessFunction(environment.getOptions());
    }
    public GP(Environment environment,Gene head){
//        this.fitnessFactory = fitnessFunctionFactory;
        this.head = head;
        this.environment = environment;
        initPredictionContainers(environment.getTrainData(),environment.getValData(), environment.getTestData());
        length = calcNrOfNodes(head);
        trainAcc=testAcc=valAcc=-1;
        ops=environment.getOptions();
        fold=""+ops.getTestFOLD();
        errorCalculator = environment.getErrorCalculator();
        initFitnessFunction(environment.getOptions());
    }
    public GP(Environment environment){
//        this.fitnessFactory = fitnessFunctionFactory;
        this.environment = environment;
        initPredictionContainers(environment.getTrainData(),environment.getValData(), environment.getTestData());
        trainAcc=testAcc=valAcc=-1;
        ops=environment.getOptions();
        fold=""+ops.getTestFOLD();
        errorCalculator = environment.getErrorCalculator();
        initFitnessFunction(environment.getOptions());
    }
    
    public void initFitnessFunction(Options options){
            try {                      
            String path="grex.fitnessfunctions.";
            if(options.getPROBLEM_TYPE()==Options.CLASSIFICATION)
                path += "Classification.";
            else
                path +="Regression.";
            
            
            if (options.isRUN_SIMPLIFICATION()) {
               fitnessFunction = (FitnessFunction)( Class.forName(path + options.getSIMPLIFICATION_FUNCTION() ).newInstance());
            } else {
               fitnessFunction = (FitnessFunction) ( Class.forName( path + options.getFITNESS_FUNCTION() ).newInstance());
            }                 
        } catch (Exception ex) {
            ErrorManager.getInstance().reportError(ex);
        }
    }

    private void initPredictionContainers(double[][] trainData,double[][] valData, double[][] testData){
        orgTrain = trainData;
        orgVal=valData;
        orgTest=testData;
        pcTrain = new PredictionContainer(trainData.length);
        pcVal = new PredictionContainer(valData.length);
        pcTest = new PredictionContainer(testData.length);
        
        Prediction p;
        for(int i=0;i<trainData.length;i++){
            p = new Prediction(trainData[i]);
                pcTrain.put(trainData[i], p );
        }
        for(int i=0;i<testData.length;i++){
            p = new Prediction(testData[i]);
            pcTest.put(testData[i], p);
        }
        for(int i=0;i<valData.length;i++){
            p = new Prediction(valData[i]);
            pcVal.put(valData[i], p);
        }
    }
    public String getName(){
        return ops.getFITNESS_FUNCTION();
    }
    
    public FitnessFunction getFitnessFunction(){
        //try {
            return fitnessFunction;// fitnessFactory.getFitnessFunction();
        /*} catch (GeneException ex) {
            ErrorManager.getInstance().reportError(ex);
            return null;
        }*/
    }
    public String getHashString(){
        return makeHashString(head);
    }
    private String makeHashString(Gene g){
        String s ="";
        Gene childs[] = g.getChildren();
        if(childs!=null){
            for(Gene c:childs){
                if(!(c instanceof TargetPred))
                    s+= makeHashString(c);
            }
        }
        return g.toString() + s;
    }
    public double calcError(String dataSet, String functionName){
        try {
            return errorCalculator.calcError(this, dataSet, functionName);
        } catch (GeneException ex) {
            ErrorManager.getInstance().reportError(ex);
        }
        return -999999;
    }

    public synchronized double calcTrainError(String functionName){
        return calcError(IErrorFunction.TRAIN, functionName);
    }
    public synchronized double calcTestError(String functionName){
        return calcError(IErrorFunction.TEST, functionName);
    }
    public synchronized double calcValError(String functionName){
        return calcError(IErrorFunction.VAL, functionName);
    }
    
    public void setOptions(Options options){
        this.ops=options;
    }
    public Options getOptions(){
        return ops;
    }
    
    //public void setFitnessFunctionFactory(FitnessFunctionFactory fitnessFactory) {
//        this.fitnessFactory = fitnessFactory;
   // }    
      

    
    public void setHead(Gene head) {
        this.head = head;
    }
    
    public void setDepth(int depth) {
        this.depth = depth;
    }
    
    public double getDepth() {
        return depth;
    }
    public void updateNodeLevels(){
        head.setNodLevel(1);
    }
    public Gene getHead(){
        return head;
    }
    
    
    public double getFitness() {
        return fitness;
    }

    public void setFitness(double fitness){
        this.fitness=fitness;
    }
    
    public synchronized List<Gene> getTreeAsList() {
        List<Gene> tree = new ArrayList<Gene>((int)length);
        getTreeAsList(tree, head);
        return tree;
    }
    
    void getTreeAsList(List<Gene> treeList, Gene gene) {
        treeList.add(gene);
        if (gene.getChildren() != null) {
            Gene[] childs = gene.getChildren();
            for (int i = 0; i < childs.length; i++) {
                getTreeAsList(treeList, childs[i]);
            }
        }
    }
    
    public void calcNrOfNodes() {
        length = calcNrOfNodes(head);
    }
    
    int calcNrOfNodes(Gene gene) {
        int currentLength = 0;
        if (gene.getChildren() != null) {
            Gene[] childs = gene.getChildren();
            int i = 0;
            if(gene instanceof If)//don't calc condition in length
                i=1;
            for (; i < childs.length; i++) {
                currentLength += calcNrOfNodes(childs[i]);
            }
        }
        return currentLength + 1;
    }

    public int calcIFs(){
        return calcIFs(head);
    }

   int calcIFs(Gene gene) {
        int currentLength = 0;
        if (gene instanceof If){
            Gene[] childs = gene.getChildren();
            currentLength += calcIFs(childs[1]);
            currentLength += calcIFs(childs[2]);
            return currentLength+1;
        }
        return 0;
    }

    public void cutTree(int depth) {//max number of consecutive ifs
            train();
        try {
            cutTree(head,depth,0);
        } catch (BNFException ex) {
            ErrorManager.getInstance().reportError(ex);
        }
            clean(); //removes possible prediction of sam class in one if
    }

   private void cutTree(Gene gene, int cutDepth, int currentDepth) throws BNFException {

        if (gene instanceof If) {
            Gene[] childs = gene.getChildren();
            if (currentDepth >= cutDepth) {
                if (!ops.getFITNESS_FUNCTION().contains("Prob")) {
                    childs[1] = new TargetPred();
                    childs[2] = new TargetPred();
                } else {
                    childs[1] = new ProbTarget();
                    childs[2] = new ProbTarget();
                }
                 childs[1].parent=gene;
                 childs[1].setEnvironment(environment);
                 childs[1].setOptions(ops);
                 childs[1].setFold(ops.getTestFOLD());
                 childs[2].setEnvironment(environment);
                 childs[2].setOptions(ops);
                 childs[2].setFold(ops.getTestFOLD());
                 childs[2].parent=gene;
            }else{
                cutTree(childs[1],cutDepth,currentDepth+1);
                cutTree(childs[2],cutDepth,currentDepth+1);
            }
        }
   }
   //Remvoes a node with two leafsnodes of the same class
   public void removeDuplicates(){
       removeDuplicated(head);
       calcNrOfNodes();
   }

    private void removeDuplicated(Gene grandParent) {
        Gene[] parents, children;

        if (grandParent instanceof If) {
            parents = grandParent.getChildren();
            for (int i = 1; i < parents.length; i++) {
                if (parents[i] instanceof If) {
                    children = parents[i].getChildren();

                    if (children[1] instanceof TargetPred) {
                        if (((TargetPred) children[1]).getCount() == 0) { // one leaf with zero instances
                            parents[i] = children[2];
             
                        }
                    }

                    if (children[2] instanceof TargetPred) {
                        if (((TargetPred) children[2]).getCount() == 0) { // one leaf with zero instances
                            parents[i] = children[1];
        
                        }
                    }
                    
                    removeDuplicated(parents[i]);
                }
            }
        }
    }
    
    public int calcDepth(){
        depth = calcDepth(head,1);
        return depth;
    }
    
    private int calcDepth(Gene parent,int depth){       
        if (parent.getChildren() != null) {
            Gene[] childs = parent.getChildren();
            int max = 0,tmp;
            for (int i = 0; i < childs.length; i++) {
                tmp = calcDepth(childs[i],depth+1);
                if(tmp>max)
                    max=tmp;
            }
            return max;
        }
        return depth;             
    }
    public int getNrOfLeaves(){
        return getNrOfLeaves(head);
    }
    
    private int getNrOfLeaves(Gene parent){
        int tmp=0;
        if (parent.getChildren() == null) {
            return 1;
        }else{
            Gene[] childs = parent.getChildren();            
            for (int i = 0; i < childs.length; i++) {
                if(!(parent instanceof If && i==0))
                tmp += getNrOfLeaves(childs[i]);                
            }            
        }
        return tmp;             
    }
    public int getNrOfIfsAndLeafs(){
        return getNrOfIfsAndLeafs(head);
    }

    private int getNrOfIfsAndLeafs(Gene parent){
        int tmp=0;
        if (parent.getChildren() == null) {
            return 1;
        }else{
            Gene[] childs = parent.getChildren();
            for (int i = 0; i < childs.length; i++) {
                if(parent instanceof If)
                tmp += 1 + getNrOfIfsAndLeafs(childs[i]);
            }
        }
        return tmp;
    }

    public String getGPTestPrediction() throws GeneException {
        String tmp = "Real\tPred\tProb\n";     
        NumberFormat nf = NumberFormat.getInstance(Locale.US);
        nf.setGroupingUsed(false);
        nf.setMaximumFractionDigits(3);   
        this.execute(pcTest);
        for (Prediction p:getOrderedTest())             
             tmp +=  nf.format(p.getPrediction()) + "\t" + nf.format(p.getTargetValue())  +"\n";// + "\t" + nf.format(p.getProbabillity())+"\n";
          //  tmp += p.getTargetValue() + "\t" + p.getPrediction();// + "\t" + p.getProbabillity()+"\n";
        return tmp;
    }

     public String getGPTrainPrediction() throws GeneException {
        String tmp = "Pred\tReal\tProb\n";
        NumberFormat nf = NumberFormat.getInstance(Locale.US);
        nf.setGroupingUsed(false);
        nf.setMaximumFractionDigits(3);
        this.execute(pcTrain);
        for (Prediction p:getOrderedTrain())
            tmp += nf.format(p.getPrediction()) +"\t" + nf.format(p.getTargetValue()) + "\n" ;// + "\t" + nf.format(p.getProbabillity())+"\n";
          //  tmp += nf.format(p.getTargetValue()) + "\t" + nf.format(p.getPrediction()) +"\n";// + "\t" + nf.format(p.getProbabillity())+"\n";
          //  tmp += p.getTargetValue() + "\t" + p.getPrediction();// + "\t" + p.getProbabillity()+"\n";
        return tmp;
    }


    public void execute(PredictionContainer pc){        
        try {
            head.execute(null,pc,Gene.TEST_MODE);
        } catch (GeneException ex) {
            ErrorManager.getInstance().reportError(ex);
        }
    }    
    public void train() {
        try {
            head.execute(null,pcTrain,Gene.TRAIN_MODE);
            resetStats();
        } catch (GeneException ex) {
            ErrorManager.getInstance().reportError(ex);
        }
    }
    public void resetStats(){
        trainAcc=-1;testAcc=-1;valAcc=-1;trainAUC=-1;valAUC=-1;testAUC=-1;
    }
     public synchronized void optimize() throws GeneException{
        head.execute(null,pcTrain,Gene.OPTIMIZE_MODE);
        train();
    }
     
    public synchronized void clean() {
        try {
            head.execute(null, pcTrain, Gene.CLEAN_MODE);
            Gene[] children = head.getChildren();
            if (children!=null && children.length == 3) {
                if (children != null && children[1] instanceof ITerminal && ((ITerminal) children[1]).getCount() == 0) {
                    head = children[2];
                }
                if (children != null && children[2] instanceof ITerminal && ((ITerminal) children[2]).getCount() == 0) {
                    head = children[1];
                }
            }
            calcNrOfNodes();
        //train();
        } catch (GeneException ex) {
            ErrorManager.getInstance().reportError(ex);
        }//*/
    train();
    }
   
    public ArrayList<Double> getGPTestPredictionAsArrayList() throws GeneException {
        ArrayList<Double> vals = new ArrayList<Double>();
        for(Prediction p:pcTest.values())                        
            vals.add(p.getPrediction());
        return vals;
    }  
    
    public ArrayList<Double> getGPValPredictionAsArrayList() throws GeneException {
        ArrayList<Double> vals = new ArrayList<Double>();        
        for(Prediction p:pcVal.values())                        
            vals.add(p.getPrediction());
        return vals;
    }   
    public ArrayList<Double> getGPTrainPredictionAsArrayList() throws GeneException {
        ArrayList<Double> vals = new ArrayList<Double>();        
        for(Prediction p:pcTrain.values())                        
            vals.add(p.getPrediction());
        return vals;
    }
    public ArrayList<Double> getRealTestAsArrayList() throws GeneException {
        ArrayList<Double> vals = new ArrayList<Double>();
        for(Prediction p:pcTest.values())   
                vals.add(p.getTargetValue());        
        return vals;
    }  
    
    public synchronized ArrayList<Double> getRealValAsArrayList() throws GeneException {
        ArrayList<Double> vals = new ArrayList<Double>();
        for(Prediction p:pcVal.values())   
                vals.add(p.getTargetValue());        
        return vals;
    }   
    public synchronized ArrayList<Double> getRealTrainAsArrayList() throws GeneException {
        ArrayList<Double> vals = new ArrayList<Double>();
        for(Prediction p:pcTrain.values())   
                vals.add(p.getTargetValue());        
        return vals;
    }    
    
    public synchronized void calcAdjustedFitness() throws GeneException, BNFException {
        calcNrOfNodes(); //Needs to be done before calcAdjusted fitness as the length is often used in the fitness function
       // fitness =  fitnessFactory.getFitnessFunction().calcFitness(this);
        fitness = fitnessFunction.calcFitness(this);
        if (fitness <= 0)
            fitness = 0.00001;
    }
    
    public synchronized double calcTestError()throws GeneException, BNFException {
       // train();
        execute(pcTest);
        return fitnessFunction.calcError(pcTest);
      // return fitnessFactory.getFitnessFunction().calcError(pcTest);
    }
    public synchronized double calcTrainError()throws GeneException, BNFException {
       // train();
        execute(pcTrain);
        return fitnessFunction.calcError(pcTrain);
      // return fitnessFactory.getFitnessFunction().calcError(pcTest);
    }
    
    
    public static double maxError=-1;
    

    
    public double getNrOfNodes() {
        return length;
    }

    public void setPcTrain(PredictionContainer train){
        pcTrain=train;
    }
        
    public String getStringTree(Gene gene) throws GeneException {
        String tmp = "";
        tmp = "( " + gene.toString();
        Gene[] childs = gene.getChildren();
        for (int i = 0; childs != null && i < childs.length; i++) {
            tmp += getStringTree(childs[i]);
        }
        
        tmp += " )";
        return tmp;
    }
    
    private static int tabs=0;
    private String getStringFormatedTree(Gene gene) throws GeneException {
        String tmp = "";
        //tmp = " (" + gene.toString();
        NumberFormat nf = NumberFormat.getInstance(Locale.US);
        nf.setMinimumFractionDigits(3);
        Gene[] childs = gene.getChildren();
        
        if (childs != null) {
            if (childs.length == 3) {
                tmp = " "+ gene.toString();
                tmp += getStringFormatedTree(childs[0]);
                tabs++;
                tmp +="\n |";
                for(int i = 0 ; i < tabs-1;i++)
                    tmp+="   |";
                tmp += "T:"+getStringFormatedTree(childs[1]);
                
                //if(childs[1].getChildren()==null)
                //    tmp += " {"+childs[1].getCorrect()+"/"+childs[1].getIncorrect()+"}";
               if(childs[1] instanceof ITerminal){
                   ITerminal term = (ITerminal) childs[1];
                    tmp += " {N="+term.getCount()+" P="+nf.format(term.getProb())+"}";
               }
                
                tmp +="\n |";
                for(int i = 0 ; i < tabs-1;i++)
                    tmp+="   |";
                tmp += "F:"+getStringFormatedTree(childs[2]);
                
                //if(childs[2].getChildren()==null)
                //    tmp += " {"+childs[2].getCorrect()+"/"+childs[2].getIncorrect()+"}";
                if(childs[2] instanceof ITerminal){
                   ITerminal term = (ITerminal) childs[2];
                    tmp += " {N="+term.getCount()+" P="+nf.format(term.getProb())+"}";
               }
                tabs--;
            }else{
                if(childs.length==2){
                    tmp = "" + getStringFormatedTree(childs[0]);
                    tmp += " " + gene.toString();
                    tmp += "" + getStringFormatedTree(childs[1]);
                }else{
                     tmp = "" + getStringFormatedTree(childs[0]);
                    tmp += " " + gene.toString();
                }
            }
        }else{
            tmp = " "+ gene.toString();
        }
        // tmp += " )";
        return tmp;
    }
    
    public String getFormatedTree() {
        String tmp="";
        try{
        tabs=0;
        if (ops.getUSED_BNF().lastIndexOf("ANN") == -1)
            tmp="\n"+getStringFormatedTree(head);
        else
            tmp="\n"+getStringTree(head);
        }catch(GeneException e){
            ErrorManager.getInstance().reportError(e);
        }
        return tmp;
    }
    
    public String getStatisticsAsString() {
        String tmp = "";
        tmp += "Fitness: " + getFitness() + "\n";
        try{
        if(ops.getPROBLEM_TYPE()==Options.CLASSIFICATION){
            tmp+= errorCalculator.getClassificationStatisitcs(this);
       /* tmp += "Train ACC: " + getTrainAcc() + "\n";
        tmp += "Train AUC:" + getTrainAUC() +"\n";
        tmp += "Train BRE:" + getTrainBRIER()+"\n\n";
        
        tmp += "Val ACC: " + getValAcc() + "\n";                
        tmp += "Val AUC:" + getValAUC() +"\n";
        tmp += "Val BRE:" + getValBRIER()+"\n\n";
        
        tmp += "Test ACC:" + getTestAcc() + "\n";
        tmp += "Test AUC:" + getTestAUC() +"\n";
        tmp += "Test BRE:" + getTestBRIER()+"\n\n";*/
        }else{
            tmp+= errorCalculator.getRegressionStatisitcs(this);
            /*tmp += "Train RMSE: " + rmseCalculator.calcRMSE(this, pcTrain) + "\n";
        tmp += "Train MUAPE:" + muapeCalculator.calcMUAPE(this, pcTrain) +"\n";
        tmp += "Train GMRAE:" + gmraeCalculator.calcGMRAE(this, pcTrain)+"\n\n";

        tmp += "Val RMSE: " + rmseCalculator.calcRMSE(this, pcVal) + "\n";
        tmp += "Val MUAPE:" + muapeCalculator.calcMUAPE(this, pcVal) +"\n";
        tmp += "Val GMRAE:" + gmraeCalculator.calcGMRAE(this, pcVal)+"\n\n";

        tmp += "Test RMSE: " + rmseCalculator.calcRMSE(this, pcTest) + "\n";
        tmp += "Test MUAPE:" + muapeCalculator.calcMUAPE(this, pcTest) +"\n";
        tmp += "Test GMRAE:" + gmraeCalculator.calcGMRAE(this, pcTest)+"\n\n";
        */}
        }catch(GeneException e){
            ErrorManager.getInstance().reportError(e);
        }//*/
        tmp += "Length: " + getNrOfNodes();
        return tmp;

    }
    
    @Override
   public synchronized Object clone(){
        PredictionContainer trn,vl,tst;
        trn= pcTrain.clone();
        vl = pcVal.clone();
        tst = pcTest.clone();

        GP newGP = new GP(length, depth, fitness, testFitness, trn, vl,
                tst,getEnvironment(),fold);

        newGP.setHead( (Gene) head.clone());
        newGP.setOptions(ops);
//        newGP.setFitnessFunctionFactory(fitnessFactory);
        newGP.orgTrain = orgTrain;
        newGP.orgVal = orgVal;
        newGP.orgTest = orgTest;
        newGP.trainAcc=trainAcc;
        newGP.testAcc=testAcc;
        newGP.valAcc=valAcc;
        newGP.trainAUC = trainAUC;
        newGP.valAUC=valAUC;
        newGP.testAUC=testAUC;
        newGP.trainBRIER=trainBRIER;
        newGP.valBRIER=valBRIER;
        newGP.testBRIER=testBRIER;
        newGP.likness=likness;
        newGP.fitness = fitness;

        return newGP;
    }

       public void copyToNewGPEnsemble(GPEnsemble newEnsemble){
        GP ens = (GP)newEnsemble;
        ens.length = length;
        ens.depth = depth;
        ens.fitness = fitness;
        ens.testFitness = testFitness;
        ens.pcTrain = pcTrain.clone();
        ens.pcTest = pcTest.clone();
        ens.pcVal = pcVal.clone();
        ens.fold = fold;
        ens.setHead( (Gene) head.clone());
        ens.setOptions(ops);
//        ens.setFitnessFunctionFactory(fitnessFactory);
        ens.orgTrain = orgTrain;
        ens.orgVal = orgVal;
        ens.orgTest = orgTest;
        ens.trainAcc=trainAcc;
        ens.testAcc=testAcc;
        ens.valAcc=valAcc;
        ens.trainAUC = trainAUC;
        ens.valAUC=valAUC;
        ens.testAUC=testAUC;
        ens.trainBRIER=trainBRIER;
        ens.valBRIER=valBRIER;
        ens.testBRIER=testBRIER;
    }

   public void setFold(String fold){
       this.fold=""+fold;
   }

   public String getFold(){
       return fold;
   }
  // public FitnessFunctionFactory gettFitnessFunctionFactory(){
  //     return fitnessFactory;
  // }
    
    public String toString() {
        try {
            tabs=0;
            return getStringTree(head);
        } catch (GeneException ex) {
            return "Fel nar trodet skulle skrivas ut: " + ex.getMessage();
        }
    }

    
    public synchronized double getTrainAccError() throws GeneException {
       // if(trainAcc==-1)
            trainAcc=calcTrainError("ACC");
        return trainAcc;
    }

    public synchronized double getTestAccError() throws GeneException {
       // if(testAcc==-1)
            testAcc = calcTestError("ACC");
        return testAcc;
    }

    public synchronized double getValAccError() throws GeneException {
       // if(valAcc==-1)
           valAcc = calcValError("ACC");
        return valAcc;
    }
    
    public synchronized double getTrainAUCError() throws GeneException{
       // if(trainAUC==-1)
            trainAUC = calcTrainError("AUC");
        return trainAUC;//
    }

    public synchronized double getTestAUCError() throws GeneException{
      //  if(testAUC==-1)
            testAUC=calcTestError("AUC");
        return testAUC;            //
    }    
    
    public synchronized double getValAUCError() throws GeneException{
      //  if(valAUC==-1)
            valAUC= calcValError("AUC");
        return valAUC;            //
    }

    public synchronized double getTrainBRIERError() throws GeneException{
    //    if(trainBRIER==-1)
            trainBRIER = calcTrainError("BRI");
        return trainBRIER;//
    }

    public synchronized double getTestBRIERError() throws GeneException{
  //      if(testBRIER==-1)
            testBRIER = calcTestError("BRI");
        return testBRIER;            //
    }    
    
    public synchronized double getValBRIERError() throws GeneException{
//       if(valBRIER==-1)
            valBRIER = calcValError("BRI");
        return valBRIER;            //
    }
    
    public synchronized Environment getEnvironment() {
        return environment;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    public PredictionContainer getPcTrain() {
        return pcTrain;
    }

    public PredictionContainer getPcVal() {
        return pcVal;
    }

    public PredictionContainer getPcTest() {
        return pcTest;
    }
    public ArrayList<Prediction> getOrderedTrain() {
        ArrayList<Prediction> ordered = new ArrayList<Prediction>();
        for (int i = 0; i < orgTrain.length; i++) {
            ordered.add(pcTrain.get(orgTrain[i]));
        }

        return ordered;
    }

    public ArrayList<Prediction> getOrderedVal() {
        ArrayList<Prediction> ordered = new ArrayList<Prediction>();
        for (int i = 0; i < orgVal.length; i++) {
            ordered.add(pcVal.get(orgVal[i]));
        }

        return ordered;
    }
    
    public String getOrderedTestPredictions() {
        ArrayList<Prediction> ordered = getOrderedTest();
        String s="Target\tPrediction\n";
        for(Prediction p:ordered){
            s+= p.getTargetValue() + "\t" + p.getPrediction() +"\n";
        }
        
        return s;
    }

    public ArrayList<Prediction> getOrderedTest() {
        ArrayList<Prediction> ordered = new ArrayList<Prediction>();
        for (int i = 0; i < orgTest.length; i++) {
            ordered.add(pcTest.get(orgTest[i]));
        }
        return ordered;
    }

    /**
     * @return the likness
     */
    public double getLikness() {
        return likness;
    }

    /**
     * @param likness the likness to set
     */
    public void setLikness(double likness) {
        this.likness = likness;
    }

    /**
     * @return the adjustedFitnessTask
     */
    public Runnable getAdjustedFitnessTask() {
        return adjustedFitnessTask;
    }
    
}
